/**
Accenture IDC
Bhumika Udani    dt: 20/07/2011 v1.0   Draft version created

**/

public with sharing class iFulfillCloneVersionWDController 
{ 
 
  Public list<stageWrapper> Stages{get;set;}
  public string asgnGrp{get;set;}
  public list<String> selectedStageNames{get;set;} 
  public list<String> selectedActiveStageNames{get;set;} 
  Public List<wrapperOut> wrapout{get;set;}
  public List<AGWrapper> asgnGroups{get;set;}
  List<AGWrapper> selectedGroups ;
  Workflow_definition__c WF; 
  Workflow_definition__c NewWF; 
  boolean flag = true; 
  list<stage_definition__c> selectedStages = new list<stage_definition__c>();
  List<Workflow_Stage__c> selStages;
  Map<ID,List<Activities_definition__c>> StepActMAP;  
  public string functionParam{get;set;}
  Map<string,id> funcNameMap =  new Map<string,id>(); 
  Map <String,List<Decimal>> sequenceStepMap;
  list<WF_Assignment_Group__c> WFGroupList ; 
  //controller
    public iFulfillCloneVersionWDController(ApexPages.StandardController controller) {
        WF = (Workflow_definition__c)controller.getRecord();
        WF.Version__c = WF.Version__c + 1;
        selectStages();
        selectedSteps();
       }
   //Select all the functions and tasks present in the current workflow definition being cloned and display    
   public void selectStages(){
       Stages =  new list<stageWrapper>();
       selectedStageNames = new list<string>();  
       selectedActiveStageNames = new list<string>();     
       set<id> stageIds = new set<id>();
       Map<ID,Decimal> stageSequenceMap = new Map<ID,Decimal>();

       selStages = [select name ,Stage__c,Stage__r.name,sequence__C from Workflow_Stage__c where workflow_code__c = :WF.Id order by sequence__C];
       List<Stage_definition__c> actStages = [select name ,sequence__C from stage_definition__c where status__c = 'active' order by sequence__C];
        //Display the active Stage Name
        for(Workflow_Stage__c ST :selStages){
            selectedStageNames.add(ST.Stage__r.name);
            stageIds.add(ST.Stage__c); 
            stageSequenceMap.put(ST.Stage__c,ST.sequence__C);

        }  
        
        for(Stage_definition__c S :actStages){
            if(flag){
            if (stageSequenceMap.containsKey(S.id)) {
            
                S.sequence__C=stageSequenceMap.get(S.id);
                Stages.add(new stageWrapper(S, true));
                selectedStages.add(S);
                selectedActiveStageNames.add(s.name);
                
                } 
            else    Stages.add(new stageWrapper(S, false)); }       
        }
     }

       public void selectedSteps(){ 
           wrapout = new list<wrapperOut>();
           set<id> stepIds = new set<id>();
           set<id> stageIds = new set<id>();  
           Map<id,id> groupIds = new Map<id,id>();

           set<integer> uniqueSequence = new set<integer>(); 
           Map<id,boolean> stepActivityMap = new Map<id,boolean>();
  
           Set<String> wfAssignmentGroup = new Set<String>();          
           StepActMAP = new Map<ID,List<Activities_definition__c>>(); 
           List<Wokflow_Steps__c> selStepList = [select id,Step__c,Default_Group__c,name,Wokflow_Stage_Name__r.name,Sequence__c,Target_Days__c,Dashboard_Function__c from Wokflow_Steps__c where Wokflow_Stage_Name__c in :selStages];        

           List<Workflow_Activities__c> selActList = [select id,name,Activity__c,Activity_Description__c, status__c,required__C,Step_Name__r.name,Step_Name__c,Step_Name__r.Wokflow_Stage_Name__r.name,Step_Name__r.Target_Days__c,Step_Name__r.Dashboard_Function__c from Workflow_Activities__c where Step_Name__c in :selStepList];           
                           
           List<Step_Definition__c> StepList = [select id,name,step_definition__c.Stage__r.name,Step_Sequence__c,Target_Days__c,Dashboard_Function__c from step_definition__c where status__c='Active' and stage__c in :selectedStages order by stage__c];        
           Map<ID, Workflow_Stage__c> map1  = new Map<ID, Workflow_Stage__c>();
           Map<ID,Wokflow_Steps__c> selectedWFStepMap = new Map<ID,Wokflow_Steps__c>();

           for(Workflow_Stage__c obj : selStages)
           {
               map1.put(obj.Stage__c, obj);
               stageIds.add(obj.id);
           }
           List<WF_Assignment_Group__c> wfAssinmentGroup = [Select  AG_Definition__c , Workflow_Function__c from WF_Assignment_Group__c where Workflow_Function__C  in :stageIds ];
           for(WF_Assignment_Group__c wfAssignGroupObj : wfAssinmentGroup  )
           {
               STring s = wfAssignGroupObj.Workflow_Function__c+'::'+ wfAssignGroupObj.AG_Definition__c;
               System.debug ( 'Both '+s);
               
               wfAssignmentGroup.add(s);
           }
           for(Wokflow_Steps__c ST :selStepList ){
               stepIds.add(ST.Step__c);
               selectedWFStepMap.put(ST.Step__c,ST);
               groupIds.put(ST.Step__c,ST.Default_Group__c);

           }
           for(Workflow_Activities__c AL :selActList ){
               stepActivityMap.put(AL.Activity__c,AL.required__C);
           }   
  
           for(Activities_definition__c ActList : [select id,name,Activity_Description__c, status__c,required__C,step__r.name,step__c,step__r.Stage__r.name,step__r.Target_Days__c,step__r.Dashboard_Function__c from Activities_definition__c where status__c = 'Active' and step__c in :StepList]){
                if(StepActMAP.containsKey(ActList.step__c)){
                    StepActMAP.get(ActList.step__c).add(ActList);//adds activities for this step to the activity list in the map    
                }else{
                    StepActMAP.put(ActList.step__c,new List<Activities_definition__c>{ActList});//adds new activity list for this step to the map    
                }                            
            }
            asgnGroups = new list<AGWrapper>();             
            for(integer i=0 ; i< Stages.size(); i++){
                  // For selected stages, check for the unique incremental sequence, disallow duplicates
                if(Stages[i].selectBox){
                
                    if(!uniqueSequence.contains(Integer.valueof(Stages[i].SD.sequence__c))){
                    
                        uniqueSequence.add(Integer.valueof(Stages[i].SD.sequence__c));
                        funcNameMap.put(Stages[i].SD.name, Stages[i].SD.id);
                        list<Assignment_Group_Name__c> Groups  = [select name, id from Assignment_Group_Name__c where function__c  INCLUDES (:Stages[i].SD.Name) and type__c = 'Fulfillment'];               
                        for(Assignment_Group_Name__c Grp:Groups){
                         //Display assignment groups for each stage selected
                            if(map1.get(Stages[i].SD.id) != null && wfAssignmentGroup.contains( map1.get(Stages[i].SD.id).id+'::'+Grp.id))
                            {
                              asgnGroups.add(new AGWrapper(Grp, true,Stages[i].SD.Name));
                            }
                            else 
                            {
                               asgnGroups.add(new AGWrapper(Grp, false,Stages[i].SD.Name));
                            }
                        }                      
                   }else{
                    ApexPages.Message msg = new ApexPages.Message(ApexPages.Severity.ERROR,'Duplicate Sequence on function');
                    ApexPages.addMessage(msg);
                    selectedStages.clear();
                   }
             }
          }       
          if(validateStageSequenceOrder()){                    
             ApexPages.Message msg = new ApexPages.Message(ApexPages.Severity.ERROR,'Please Check Function Sequence Order');
             ApexPages.addMessage(msg);   
             return;       
          }
          for(Step_Definition__c stdef :StepList){
                wrapperOut tmpwrapper = new wrapperOut();
                list<SelectOption> tskGrpOptions = new list<SelectOption>();
                for(AGWrapper Gp :asgnGroups)
                {
                  if(stdef.Stage__r.name == Gp.functionName && Gp.selectbox){
                           tskGrpOptions.add(new SelectOption(Gp.AGroup.id,Gp.AGroup.Name)); 
                   }  
                     
                }  
                if(selectedWFStepMap.containsKey(stdef.id)){ 
                    Wokflow_Steps__c wfStep= selectedWFStepMap.get(stdef.id);
                    stdef.Step_Sequence__c=wfStep.Sequence__c;
                    stdef.Target_Days__c=wfStep.Target_Days__c;
                    stdef.Dashboard_Function__c=wfStep.Dashboard_Function__c;                
                    tmpwrapper.Step=new stepWrapper(stdef, true,groupIds.get(stdef.id),tskGrpOptions);
                 }                       
                else
                    tmpwrapper.Step=new stepWrapper(stdef, false,'--None--',tskGrpOptions);
                                    
                List<ActWrapper> twrap2=new List<ActWrapper>(); 
                if(StepActMAP.containsKey(stdef.id)){
                    for(Activities_definition__c ActDef : StepActMAP.get(stdef.id)){
                        if(stepActivityMap.containsKey(ActDef.id)){
                            ActDef.required__C = stepActivityMap.get(ActDef.id);
                            twrap2.add(new ActWrapper(ActDef, True));
                        }   
                        else
                        twrap2.add(new ActWrapper(ActDef, False));
                    }
                }
                tmpwrapper.Act = twrap2 ;                
                wrapout.add(tmpwrapper);
            }
        }
    private void saveSelectedAssignmentGroup()
    {
       selectedGroups = new List<AGWrapper>(); 
       functionParam = '';    
       for(AGWrapper A: asgnGroups)
       {
           if(A.selectbox) { functionParam = functionParam + A.AGroup.Name; selectedGroups.add(A);}
       }
    }
  
        
    public pagereference saveGroup(){
     saveSelectedAssignmentGroup();
     if(selectedGroups.size()>0){
        wrapout = new list<wrapperOut>();
        StepActMAP = new Map<ID,List<Activities_definition__c>>();
        List<Step_definition__c> StepList = [select id,name,step_definition__c.Stage__r.name,Step_Sequence__c,Target_Days__c,Dashboard_Function__c from step_definition__c where status__c='Active' and stage__c = :selectedStages order by stage__c];        
        for(Activities_definition__c ActList : [select id,name,Activity_Description__c, status__c,required__C,step__r.name,step__c,step__r.Stage__r.name,step__r.Target_Days__c,step__r.Dashboard_Function__c from Activities_definition__c where status__c = 'Active' and step__c in :StepList]){
            if(StepActMAP.containsKey(ActList.step__c)){
                StepActMAP.get(ActList.step__c).add(ActList);//adds activities for this step to the activity list in the map    
            }else{
                StepActMAP.put(ActList.step__c,new List<Activities_definition__c>{ActList});//adds new activity list for this step to the map    
                }                            
         }           
         for(Step_definition__c stdef :StepList)
         {
             wrapperOut tmpwrapper = new wrapperOut();            
             list<SelectOption> tskGrpOptions = new list<SelectOption>();
             for(AGWrapper Gp :selectedGroups)
             {
                 System.debug ( 'sssss'+Gp.functionName + '---'+ gp.selectBox);
                 if(stdef.Stage__r.name == Gp.functionName  && gp.selectBox)
                 {
                     tskGrpOptions.add(new SelectOption(Gp.AGroup.id,Gp.AGroup.Name)); 
                 }
             }                            
             tmpwrapper.Step=new stepWrapper(stdef, false,'--None--',tskGrpOptions);
             List<ActWrapper> twrap2=new List<ActWrapper>(); 
             if(StepActMAP.containsKey(stdef.id))
             {
                 for(Activities_definition__c ActDef : StepActMAP.get(stdef.id))
                 {
                     twrap2.add(new ActWrapper(ActDef, False));
                 }
              }
                  tmpwrapper.Act = twrap2 ;                
                  wrapout.add(tmpwrapper);
            }         
          return null; 
        }
        else{  
              ApexPages.Message msg = new ApexPages.Message(ApexPages.Severity.ERROR,'Please select atleast one Assignment Group Per Function');          
              ApexPages.addMessage(msg); 
              return null;
         }   
  
     }   
     public Class AGWrapper{
        public Assignment_Group_Name__c AGroup{get;set;}
        public boolean selectbox{get;set;}
        public string functionName{get;set;}        
        public AGWrapper(Assignment_Group_Name__c G, boolean chkbox, string func){
            AGroup=G;
            selectbox =chkbox;
            functionName = func;
        }
    }
    //Wrapper classes to bind the selectboxes with the records                
        
    public Class StepWrapper{
        public Step_Definition__c StepDef{get;set;}
        public boolean selbox{get;set;} 
        public string defaultAG{get;set;}
        public List<SelectOption> funcGroups{get;set;}       
        public stepWrapper(Step_Definition__c S, boolean chkbox){
            StepDef =S;
            selbox =chkbox;
        }
        public stepWrapper(){}
        public stepWrapper(step_definition__c S, boolean chkbox, string defaultGrp,List<SelectOption> funcGrps ){
            StepDef =S;
            selbox =chkbox;
            defaultAG = defaultGrp;
            funcGroups = funcGrps ;
        }
    }
    
   public  Class ActWrapper{
        public Activities_definition__c AD{get;set;}
        public boolean slctbox{get;set;}        
        public ActWrapper(Activities_definition__c A, boolean chkbox){
            AD =A;
            slctbox =chkbox;
        }
        public ActWrapper(){}
    }
    
    public Class WrapperOut{
        public List<ActWrapper> Act{get;set;}
        public stepWrapper step{get;set;}        
        public wrapperOut(){
            if(step==null){step= new stepWrapper();}
            if(Act==null){Act= new List<ActWrapper>();}           
        }
    }                   
        
    public Class stageWrapper{
        public stage_definition__c SD{get;set;}
        public boolean selectbox{get;set;}        
        public stageWrapper(stage_definition__c S, boolean chkbox){
            SD =S;
            selectbox =chkbox;
        }
    }
    private boolean validateStageSequenceOrder()
    {      
          Decimal maximumSequence =0.0;          
          for(stage_definition__c S:selectedStages){
           if(S.Sequence__c >maximumSequence){
               maximumSequence =S.Sequence__c;
           }            
          }          
          if(maximumSequence.intValue() !=  selectedStages.size()){
             return true;
          }  
          return false; 
    } 
    private boolean validateStepSequenceOrder(){
      for(stage_definition__c S:selectedStages){
        List<Decimal> stepList = new List<Decimal>();
           if(sequenceStepMap.containsKey(S.id)){
               stepList = sequenceStepMap.get(S.id);
               System.debug('*******'+stepList+'size'+stepList.size());
               if(stepList!=null && stepList.size()>0){
                   stepList.sort();
                   if(stepList.size() !=stepList[stepList.size()-1].intValue()){
                       return true;
                   }
               }
           }     
       }            
      return false;
   }
   private void  insertWorkflow(){   
     try
       {         
         NewWF = new Workflow_definition__c();
         NewWF.Workflow_Name__c = WF.Workflow_Name__c;
         NewWF.Status__c = IfullfillTextConstant.WF_DEF_BUILDING_IN_PROGRESS_STATUS;
         NewWF.Workflow_Description__c = WF.Workflow_Description__c;
         NewWF.Version__c = WF.Version__c ;
         NewWF.Derived_from__c = WF.ID;
         insert NewWF; 
       }
       Catch(DMLException e){GeneralConstants.mailAnyExceptionToMe('WorkFLowCOntroller','SaveSteps: Insert Stages failed',e);}
   }
   private void insertWorkflowFunctions(){   
      list<Workflow_Stage__c> WFStageList = new list<Workflow_Stage__c>();
      set<Decimal> fsequenceSet = new set<Decimal>();
      for(stage_definition__c S:selectedStages){        
         Workflow_Stage__c WFStage = new Workflow_Stage__c();
         WFStage.Workflow_Code__c = NewWF.id;
         WFStage.Name = S.name;
         WFStage.Stage__c = S.id;                
         WFStage.sequence__C = S.sequence__C;
         WFStageList.add(WFStage); 
      }     
      if(WFStageList.size()>0){
         try{insert WFStageList;}
         Catch(DMLException e){GeneralConstants.mailAnyExceptionToMe('WorkFLowCOntroller','SaveSteps: Insert Stages failed',e);}      
      }
    } 
    private void inserWFGroups(){    
        WFGroupList = new list<WF_Assignment_Group__c>(); 
        Map<string,id> stageNameMap =  new Map<string,id>();         
        list<Workflow_Stage__c> StageId = [select id, stage__c, name from Workflow_Stage__c where Workflow_Code__c = :NewWF.id];                  
        saveSelectedAssignmentGroup();
        for(Workflow_Stage__c s: StageId)
        {
             stageNameMap.put(s.name, s.id);
        }
        for(AGWrapper grp :selectedGroups)
        {
              WF_Assignment_Group__c WFgrp = new WF_Assignment_Group__c();
              WFgrp.Name = grp.AGroup.Name;
              WFgrp.AG_Definition__c = grp.AGroup.id;
              WFgrp .Workflow_Function__c = stageNameMap.get(grp.functionName);
              WFGroupList.add(WFgrp); 
        }
        if (WFGroupList.size()>0 && WFGroupList[0].id == null)
        {
            system.debug('groupList'+WFGroupList);
            try{insert WFGroupList;}
            catch(dmlexception e){GeneralConstants.mailAnyExceptionToMe('WorkFLowCOntroller','SaveGroup: Insert Groups failed',e);}
        }  
    
    }
    private boolean insertWFTask(){   
        set<string> uniqueSeq = new set<string>();
        sequenceStepMap= new Map <String,List<Decimal>>(); 
        list<Wokflow_Steps__c> WFStepList = new list<Wokflow_Steps__c>();
        map<id,id> stageMap = new Map<id,id>();
        list<Workflow_Stage__c> StageIds = [select id, stage__c from Workflow_Stage__c where Workflow_Code__c = :NewWF.id];
        for(Workflow_Stage__c s: StageIds){
            stageMap.put(s.stage__c,s.id);
        } 
        for(integer i=0 ; i< wrapOut.size(); i++){
            if(wrapOut[i].Step.selbox)
            {
                 if(sequenceStepMap.containsKey(wrapOut[i].Step.StepDef.stage__c)){
                    sequenceStepMap.get(wrapOut[i].Step.StepDef.stage__c).add(wrapOut[i].Step.StepDef.step_sequence__C);
                 }else{
                    sequenceStepMap.put(wrapOut[i].Step.StepDef.stage__c,new List<Decimal>{wrapOut[i].Step.StepDef.step_sequence__C});
                 }
                 if(!uniqueSeq.contains(wrapOut[i].Step.StepDef.stage__c + String.valueof(Integer.valueof(wrapOut[i].Step.StepDef.step_sequence__C))))
                 {
                    uniqueSeq.add(wrapOut[i].Step.StepDef.stage__c + String.valueof(Integer.valueof(wrapOut[i].Step.StepDef.step_sequence__C)));
                    Wokflow_Steps__c WFStep = new Wokflow_Steps__c();
                    WFStep.Wokflow_Stage_Name__c = stageMap.get(wrapOut[i].Step.StepDef.stage__c);
                    WFStep.Name = wrapOut[i].Step.StepDef.name;
                    WFStep.Step__c = wrapOut[i].Step.StepDef.id;
                    WFStep.sequence__C = (wrapOut[i].Step.StepDef.step_sequence__C);
                    WFStep.Target_Days__c = wrapOut[i].Step.StepDef.Target_Days__C;
                    WFStep.Dashboard_Function__c = wrapOut[i].Step.StepDef.Dashboard_Function__C;
                    WFStep.Default_Group__c =  wrapOut[i].Step.defaultAG ;
                    WFStepList.add(WFStep);
                }
                else
                {
                     WFStepList.clear();
                     ApexPages.Message msg = new ApexPages.Message(ApexPages.Severity.ERROR,'Duplicate Sequence On Tasks');
                     ApexPages.addMessage(msg);
                     return true;
                }
            }
        }
        if(validateStepSequenceOrder()){ 
             ApexPages.Message msg = new ApexPages.Message(ApexPages.Severity.ERROR,'Please Check Task Sequence Order');
             ApexPages.addMessage(msg);    
             return true;                      
        }
       
        if(WFStepList.size()>0){
            try{Insert WFStepList;}
            catch(dmlException e){GeneralConstants.mailAnyExceptionToMe('WorkFLowController','SaveSteps: Insert Steps failed',e);
            }
        } 
      return false;  
    }
    private void insertWFActivities(){    
        list<Workflow_Activities__c> WFActList = new list<Workflow_Activities__c>();
        map<id,id> stepMap = new Map<id,id>();
        list<Wokflow_Steps__c> StepIds = [select id, step__c from Wokflow_Steps__c where Wokflow_Stage_Name__r.Workflow_Code__c = :NewWF.id];
        for(Wokflow_Steps__c  s: StepIds ){
            stepMap.put(s.step__c,s.id);
        } 
        for(integer i=0 ; i< wrapOut.size(); i++){
         for(integer j=0 ; j< wrapOut[i].Act.size(); j++){
            if(wrapOut[i].Act[j].slctbox){
                Workflow_Activities__c WFAct = new Workflow_Activities__c();
                WFAct.Step_Name__c = stepMap.get(wrapOut[i].Act[j].AD.step__c);
                WFAct.Name = wrapOut[i].Act[j].AD.name;
                WFAct.Activity__c = wrapOut[i].Act[j].AD.id;
                WFAct.required__C = boolean.valueof(wrapOut[i].Act[j].AD.Required__C);
                WFActList .add(WFAct);
            }}
        }       
        if(WFActList.size()>0){
            try{Insert WFActList;}
            catch(dmlException e){GeneralConstants.mailAnyExceptionToMe('WorkFLowController','SaveSteps: Insert Steps failed',e);
            }
        } 
    }
    public pagereference  saveUpdatedWFDefn(){       
        //Save Workflow 
        insertWorkflow();
        //Save Stages
        insertWorkflowFunctions();
        //Save Groups
        inserWFGroups();
        //Save Steps 
        if(insertWFTask()) return null; 
        //Save Activities
        insertWFActivities();
        
        WF.Version__c = WF.Version__c - 1;
        WF.Status__c = IfullfillTextConstant.WF_DEF_PENDING_IN_ACTIVATION_STATUS;
        update WF;
        Pagereference Page = new Pagereference(IfulfillVFPagesName.WF_DEF_SUMMARY_PAGE +'?id='+NewWF.id); 
        iFulfillHandler.updateLastTaskAndFunction(NewWF.Id);
        return Page;
    } 
    
     public pagereference saveFunctions(){
        flag = false;
        selectedStageNames.Clear();
        selectedStages.Clear();
        for(integer i=0 ; i< Stages.size(); i++){
            if(Stages[i].selectBox){
                selectedStageNames.add(Stages[i].SD.name);
                selectedStages.add(Stages[i].SD);                 
            }
        }
        selectedSteps();
        return null;
    }     

}